//
// Copyright 2019 Pixar
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
//    names, trademarks, service marks, or product names of the Licensor
//    and its affiliates, except as required to comply with Section 4(c) of
//    the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#ifndef PXR_EXTRAS_USD_EXAMPLES_USD_DANCING_CUBES_EXAMPLE_DATA_H
#define PXR_EXTRAS_USD_EXAMPLES_USD_DANCING_CUBES_EXAMPLE_DATA_H

#include "pxr/pxr.h"
#include "pxr/usd/sdf/api.h"
#include "pxr/usd/sdf/abstractData.h"
#include "pxr/usd/sdf/fileFormat.h"
#include "pxr/base/tf/declarePtrs.h"
#include "pxr/base/tf/token.h"
#include "pxr/base/vt/value.h"

#include <vector>

PXR_NAMESPACE_OPEN_SCOPE

class UsdDancingCubesExample_DataImpl;

TF_DECLARE_WEAK_AND_REF_PTRS(UsdDancingCubesExample_Data);

// Macro input defining the parameter fields in the data's params struct.
// It's easier to define the fields in the params structure via macro so that 
// we can easily add or remove parameters without having to update all the
// functions for converting between file format arguments and dictionary values.
// xx(TYPE, NAME, DEFAULT_VALUE)
#define USD_DANCING_CUBES_EXAMPLE_DATA_PARAMS_X_FIELDS \
    xx(int, perSide, 1) \
    xx(int, numFrames, 100) \
    xx(int, framesPerCycle, 24) \
    xx(double, distance, 10.0) \
    xx(double, moveScale, 1.0) \
    xx(TfToken, geomType, "Cube")

// A token of the same name must be defined for each parameter in the macro 
// above so we can access these parameter values in file format arguments and
// VtDictionary values.
#define USD_DANCING_CUBES_EXAMPLE_DATA_PARAMS_TOKENS \
    (perSide) \
    (numFrames) \
    (framesPerCycle) \
    (distance) \
    (moveScale) \
    (geomType)

TF_DECLARE_PUBLIC_TOKENS(UsdDancingCubesExample_DataParamsTokens,
                         USD_DANCING_CUBES_EXAMPLE_DATA_PARAMS_TOKENS);

// The parameter structure used as the input values to generate the data's 
// specs and fields procedurally. This is converted to and from file format
// arguments.
struct UsdDancingCubesExample_DataParams {
    
    // Define each parameter declared in the param field macro. 
    #define xx(TYPE, NAME, DEFAULT) TYPE NAME {DEFAULT};
    USD_DANCING_CUBES_EXAMPLE_DATA_PARAMS_X_FIELDS
    #undef xx       

    // Creates a new params structure from the given file format arguments.
    static UsdDancingCubesExample_DataParams FromArgs(
        const SdfFileFormat::FileFormatArguments & args);

    // Creates a new params structure from a VtDictionary
    static UsdDancingCubesExample_DataParams FromDict(
        const VtDictionary& dict);

    // Converts this params structure into the file format arguments that could 
    // be used to recreate these parameters.
    SdfFileFormat::FileFormatArguments ToArgs() const;
};

/// \class UsdDancingCubesExample_Data
///
/// This is the derived class of SdfAbstractData that 
/// UsdDancingCubesExampleFileFormat uses for the contents of layers opened
/// from files of that format. This data is initialized with the small set
/// of parameters from UsdDancingCubesExample_DataParams which will be populated
/// by a layer's file format arguments. These params are used to procedurally
/// generate the specs, fields, and time samples when requested from the layer 
/// without the layer having any file contents backing it whatsoever. Given that
/// this layer data is completely generated from arguments, it is also read 
/// only, so all the spec editing operations are disabled for these layers. Note
/// that this class provides the interface required by SdfAbstractData; the 
/// implementation details are all provided by UsdDancingCubesExample_DataImpl.
/// 
/// The scene that is generated by this data object is a number of cube prims
/// arranged in the layout of a larger cube, equally spaced from each other. 
/// These cubes are animated to move, rotate, and change color in a manner 
/// related to their positions within the larger cube.
/// 
/// The procedural parameters that affect the layout and animation of these 
/// cubes are defined in UsdDancingCubesExample_DataParams and behave as such:
/// 
///   perSide - The number of cubes arranged on each side of the layout. E.g. a
///             value of 3 gives you 27 animated cubes in a 3x3x3 cube layout.
///   distance - How far part each cube is from each other in the layout.
///   numFrames - The total number of frames of time samples for the animation. 
///               The animation loops based on the value of framesPerCycle.
///   framesPerCycle - How many frames of time samples there are in a single 
///                    cycle of the animation.
///   moveScale - A scale factor to the distance the cubes move when animated.
///   geomType - A token of the name the geometry type. Valid names are of other
///              UsdGeom types such as "Cone", "Cylinder", or "Capsule"
/// 
class UsdDancingCubesExample_Data : public SdfAbstractData
{
public:
    /// Factory New. We always create this data with an explicit params object.
    static UsdDancingCubesExample_DataRefPtr New(
        const UsdDancingCubesExample_DataParams &params);

    virtual ~UsdDancingCubesExample_Data();

    /// SdfAbstractData overrides
    bool StreamsData() const override;

    bool IsEmpty() const override;

    void CreateSpec(const SdfPath& path, 
                    SdfSpecType specType) override;
    bool HasSpec(const SdfPath& path) const override;
    void EraseSpec(const SdfPath& path) override;
    void MoveSpec(const SdfPath& oldPath, 
                  const SdfPath& newPath) override;
    SdfSpecType GetSpecType(const SdfPath& path) const override;

    bool Has(const SdfPath& path, const TfToken &fieldName,
             SdfAbstractDataValue* value) const override;
    bool Has(const SdfPath& path, const TfToken& fieldName,
             VtValue *value = NULL) const override;
    VtValue Get(const SdfPath& path, 
                const TfToken& fieldName) const override;
    void Set(const SdfPath& path, const TfToken& fieldName,
             const VtValue & value) override;
    void Set(const SdfPath& path, const TfToken& fieldName,
             const SdfAbstractDataConstValue& value) override;
    void Erase(const SdfPath& path, 
               const TfToken& fieldName) override;
    std::vector<TfToken> List(const SdfPath& path) const override;

    std::set<double> ListAllTimeSamples() const override;
    
    std::set<double> ListTimeSamplesForPath(
        const SdfPath& path) const override;

    bool GetBracketingTimeSamples(
        double time, double* tLower, double* tUpper) const override;

    size_t GetNumTimeSamplesForPath(
        const SdfPath& path) const override;

    bool GetBracketingTimeSamplesForPath(
        const SdfPath& path, double time, 
        double* tLower, double* tUpper) const override;

    bool QueryTimeSample(const SdfPath& path, double time,
                         SdfAbstractDataValue *optionalValue) const override;
    bool QueryTimeSample(const SdfPath& path, double time, 
                         VtValue *value) const override;

    void SetTimeSample(const SdfPath& path, double time, 
                       const VtValue & value) override;

    void EraseTimeSample(const SdfPath& path, double time) override;

protected:
    // SdfAbstractData overrides
    void _VisitSpecs(SdfAbstractDataSpecVisitor* visitor) const override;

private:
    // Private constructor for factory New
    UsdDancingCubesExample_Data(
        const UsdDancingCubesExample_DataParams &params);

    // Pointer to the actual implementation
    std::unique_ptr<UsdDancingCubesExample_DataImpl> _impl;
};

PXR_NAMESPACE_CLOSE_SCOPE

#endif // PXR_EXTRAS_USD_EXAMPLES_USD_DANCING_CUBES_EXAMPLE_DATA_H
